package design.pattern.Lu12StrategyPattern;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.reflections.Reflections;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;

/**
 * @program JavaBase
 * @description: 策略模式 - 优惠折扣
 * @author: zhanglu
 * @create: 2022-05-19 18:47:00
 */
@Slf4j
public class StrategyPattern03 {

    /***************场景***************/

    interface ICouponDiscount<T> {
        BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);
    }

    /***************策略实现***************/

    static class MJCouponDiscount implements ICouponDiscount<Map<String,String>> {
        /**
         * 满减计算
         * 1. 判断满足x元后-n元，否则不减
         * 2. 最低支付金额1元
         */
        @Override
        public BigDecimal discountAmount(Map<String, String> couponInfo, BigDecimal skuPrice) {
            String x = couponInfo.get("x");
            String o = couponInfo.get("n");
            // 小于商品金额条件的，直接返回商品原价
            if (skuPrice.compareTo(new BigDecimal(x)) < 0) return skuPrice;
            // 减去优惠金额判断
            BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));
            if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
            return discountAmount;
        }
    }

    static class NYGCouponDiscount implements ICouponDiscount<Double> {
        /**
         * n元购购买
         * 1. 无论原价多少钱都固定金额购买
         */
        public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
            return new BigDecimal(couponInfo);
        }
    }

    static class ZJCouponDiscount implements ICouponDiscount<Double> {
        /**
         * 直减计算
         * 1. 使用商品价格减去优惠价格
         * 2. 最低支付金额1元
         */
        public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
            BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));
            if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
            return discountAmount;
        }
    }

    static class ZKCouponDiscount implements ICouponDiscount<Double> {
        /**
         * 折扣计算
         * 1. 使用商品价格乘以折扣比例，为最后支付金额
         * 2. 保留两位小数
         * 3. 最低支付金额1元
         */
        public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
            BigDecimal discountAmount = skuPrice.multiply(new BigDecimal(couponInfo)).setScale(2, BigDecimal.ROUND_HALF_UP);
            if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
            return discountAmount;
        }
    }

    /***************调度***************/
    static class Context<T> {
        private Method method;
        Map<String, T> implMap = new LinkedHashMap<>();
        public Context(Class<T> interfaces) throws Exception {
            // 扫描IDataValidator所在的包 com.lm.validate
            Reflections reflections = new Reflections(interfaces.getPackage().getName());
            // 获取包com.lm.validate下面所有IDataValidator实现类
            Set<Class<? extends T>> implClass = reflections.getSubTypesOf(interfaces);
            for (Class<? extends T> aClass : implClass) {
                implMap.put(aClass.getName(), aClass.newInstance());
            }
            this.method = interfaces.getMethods()[0];
        }
        public <R> R execute(Class<? extends T> aClass, String method, Object[] args) throws Exception {
            T t = implMap.get(aClass.getName());
            Class[] classes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                classes[i] = args[i].getClass();
            }
            return (R) t.getClass().getMethod(method, classes).invoke(t, args);
        }
        public <R> R execute(Class<? extends T> aClass, Object[] args) throws Exception {
            T t = implMap.get(aClass.getName());
            Class[] classes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                classes[i] = args[i].getClass();
            }
            return (R) method.invoke(t, args);
        }
    }



    @Test
    public void test01() throws Exception {
        Context context = new Context<>(ICouponDiscount.class);

        BigDecimal discountAmount = (BigDecimal) context.execute(ZJCouponDiscount.class, new Object[]{10D, new BigDecimal(100)});
        log.info("测试结果：优惠后金额 {}", discountAmount);

        Map<String,String> mapReq = new HashMap<String, String>();
        mapReq.put("x","100");
        mapReq.put("n","10");
        discountAmount = (BigDecimal) context.execute(MJCouponDiscount.class, new Object[]{mapReq, new BigDecimal(100)});
        log.info("测试结果：优惠后金额 {}", discountAmount);

        discountAmount = (BigDecimal) context.execute(ZKCouponDiscount.class, new Object[]{0.9D, new BigDecimal(100)});
        log.info("测试结果：优惠后金额 {}", discountAmount);

        discountAmount = (BigDecimal) context.execute(NYGCouponDiscount.class, new Object[]{90D, new BigDecimal(100)});
        log.info("测试结果：优惠后金额 {}", discountAmount);

        discountAmount = (BigDecimal) context.execute(NYGCouponDiscount.class, "discountAmount", new Object[]{90D, new BigDecimal(100)});
        log.info("测试结果：优惠后金额 {}", discountAmount);

    }

}
